#ifndef GPULAYOUT_H
#define GPULAYOUT_H

#include "GM3Layout.h"  //#include "Layout.h"
#include "KDTree.h"

#include <CL/cl.h>

//namespace GM3{

class GPULayout: public GM3Layout
{
    public:
        struct __attribute__((aligned(4))) kdnode{
            cl_float x;
            cl_float y;
            cl_float d;
            cl_float w;
            cl_uint s;
            cl_uint t;
            cl_int l;
            cl_int r;
        };

        static unsigned int n;

        GPULayout(const char *fn=0);
        GPULayout(GM3Graph *g);

        ~GPULayout();

        //void loadGraph(GM3Graph *g);
        void loadGraph(GM3Graph *g, unsigned int N=0, unsigned int E=0, bool ifRandomize = false);

        GM3Graph *computeLayout();
        GM3Graph *computeLayoutTS(GM3Graph *GTn, std::vector<int> tmap);

    private:
        std::vector<GM3Graph> graphTimeList;
        std::vector<unsigned int> trans;

        //OpenCL state variables
        cl_context context;
        cl_device_id *devices;
        cl_command_queue commandQueue;
        cl_program OCLProg;

        //OCL kernel handles
        cl_kernel stepKern;
        cl_kernel timestepKern;
        
        //OpenCL memory arrays
        cl_mem nodes_b;
        cl_mem nodestm1_b;
        cl_mem tmap_b;
        cl_mem CSRN_b;
        cl_mem CSRE_b;
        cl_mem nodeW_b;
        cl_mem edgeW_b;
        cl_mem KDNodes_b;

        //CPU-side data formatted for the GPU - keeping these buffers reduces
        //the number of allocations. Buffers are kept only for frequently 
        //rewritten data.
        std::vector<cl_float> GPUTmp; //size of the larger of |N|*2 and |E|
        std::vector<kdnode> GPUKDNodes;
        std::vector<cl_uint> CSRNodes;
        std::vector<cl_uint> CSREdges;

        //cleanup
        void cleanupCL();
        void cleanupCLMem();

        //CSR format--will be used internally by the GPU
        // - 'Compressed Sparse Row'
        // - The node list holds the starting location of the adj list for each
        //   node. Each adjacency list is bound by the start of the next list.
        // - The edge list is a sequential set of adjacency lists
        void buildCSR();

        //initialization - in order of increasing frequency
        void initOnce();
        void initPerGraph(unsigned int N, unsigned int E);
        void initPerLayout();
        unsigned int initPerLevel(unsigned int level);
        void initPerKDTree();
        void initPerStep();

        bool KDRebuildStep(unsigned int step, unsigned int lastStep);
        std::vector<unsigned int> buildKDTree();
        void layoutLevel();
        void layoutStep();

        void layoutLevelTS(std::vector<int> &tmap);
        void layoutStepTS();

        //graph conversion
        void convertGraphNodePosToGPU(std::vector<GM3Graph::node> &nodes,
                std::vector<cl_float> &pos);
        void convertGraphNodePosToCPU(std::vector<cl_float> &pos,
                std::vector<GM3Graph::node> &nodes);
        void convertGraphNodeWToGPU(std::vector<GM3Graph::node> &nodes,
                std::vector<cl_float> &weights);
        void convertGraphEdgeWToGPU(std::vector<GM3Graph::edge> &edges,
                std::vector<cl_float> &weights);


        //multipole
        kdnode convertKDNode(KDTree::node *);
        void convertKDTree(KDTree *, std::vector<kdnode> &);
        void rStacklessConv(std::vector<GPULayout::kdnode> &, int, int);
};

//}

#endif
